#!/usr/bin/python
from plistlib import readPlist
from plistlib import writePlist

import getopt
import os.path
import shelve
import shutil
import sys
import urllib
import urlparse

def uri2path(uri):
    parsedUrl = urlparse.urlparse(uri)
    path = parsedUrl[2]
    if sys.platform in ('cygwin', 'win32') :
        #Drop the leading '/'
        path = path[1:]
    return urllib.url2pathname(path)

def builddb(opts):
    pl = readPlist(opts['--source-xml'])
    lib = shelve.open(opts['--dest-db'], 'c', 2)
    lib.update(pl)
    return None

def readdb(opts):
    lib = shelve.open(opts['--source-db'], 'r', 2)
    return lib

def prettyTrail(depth):
    return "/%s" % "/".join(depth)

def recurseDiff(merge, old, new, depth):
    tab = "   " * len(depth)
    if old == None:
        print prettyTrail(depth)
        print tab, "only in new"
        return
    if new == None:
        print prettyTrail(depth)
        print tab, "only in old"
        return
    if old != new:
        print prettyTrail(depth)
        print tab, "old:", old, type(old)
        print tab, "new:", new, type(new)

        

def getDictItem(itemContainer, indexKey):
    if itemContainer != None:
        return itemContainer.get(indexKey, None)
    return None

def getListItem(itemContainer, indexKey):
    if itemContainer != None and indexKey < len(itemContainer) and indexKey > 0:
        return itemContainer[indexKey]
    return None
def recurse(callback, merge, old, new, depth=()):
    if hasattr(merge, 'has_key'):
        #print tab, k, "I am a dict type"
        for k in merge:
            recurse(callback, merge[k], getDictItem(old, k), getDictItem(new, k), depth+(k,))
    elif hasattr(merge, 'sort'):
        #print tab, k, "I am a list type"
        for i in range(len(merge)):
            recurse(callback, merge[i], getListItem(old, i), getListItem(new, i), depth)
    else:
        callback(merge, old, new, depth)

def diff(opts):
    oldLib = shelve.open(opts['--old-db'], 'r', 2)
    newLib = shelve.open(opts['--new-db'], 'r', 2)

    merge = dict(oldLib)
    merge.update(newLib)

    recurse(recurseDiff, merge, oldLib, newLib)

def setKey(db, depth, dictKey, value):
    key = db
    for k in depth:
        if key.has_key(k):
            key = key[k]
        else:
            key = None
            break
    if key != None:
        key[dictKey] = value
    return key
    
class exporter:
    def __init__(self, srcDir, dstDir, locDir, db):
        self.srcDir = srcDir
        self.dstDir = dstDir
        self.locDir = locDir
        self.db = db
    def recurseExport(self, db, old, new, depth):
        #print "poop db", id(self.db), 'tracks', id(self.db['Tracks']), depth
        if depth[0] == 'Tracks' and depth[-1] == 'Location':
            path = uri2path(db)
            relPath = path.replace(self.srcDir, '')
            exportPath = urlparse.urljoin('file://localhost/', urllib.pathname2url(os.path.join(self.locDir, relPath)))
            key = setKey(self.db, depth[:-1], 'Location', exportPath)
            fileDest = os.path.join(self.dstDir, relPath)
            dirDest = os.path.dirname(fileDest)
            #print "fileDest:", fileDest
            #print "dirDest:", dirDest
            if not os.path.exists(dirDest):
                os.makedirs(dirDest)
            try:
                shutil.copy(path, fileDest)
            except:
                print "Couldn't copy path '%s' to dest '%s' at depth '%s'" % (path, fileDest, depth)

            
def export(opts):
    #copy the entire db
    db = dict(shelve.open(opts['--source-db'], 'r', 2))
    dstDir = opts['--dest-dir']
    locDir = opts['--relocate-dir']
    if not db.has_key('Music Folder'):
        print "Can't find Music Folder in db"
        return
    e = exporter(uri2path(db['Music Folder']), dstDir, locDir, db)

    if os.path.exists(dstDir) and os.path.isdir(dstDir):
        print "Dest dir:", dstDir
        recurse(e.recurseExport, db, None, None)
        writePlist(e.db, os.path.join(dstDir, 'export.xml'))
    else:
        print "Invalid dir", dstDir

commands = {
        'builddb' : (builddb, 'source-xml=', 'dest-db='),
        'readdb'  : (readdb,  'source-db='),
        'diff'    : (diff, 'old-db=', 'new-db='),
        'export'  : (export, 'source-db=', 'dest-dir=', 'relocate-dir='),
        }
commandKeys = ('builddb', 'readdb', 'diff', 'export')
def usage():
    print "%s: usage" % sys.argv[0]

    for key in commandKeys:
        args = list(commands[key][1:])
        for i in range(len(args)):
            arg = args[i]
            argType = ''
            if arg.find('db=') != -1:
                argType = '<db file>'
            elif arg.find('dir=') != -1:
                argType = '<directory>'
            elif arg.find('xml=') != -1:
                argType = '<xml file>'
            else:
                raise "unknown arg type"
            args[i] = '--' + arg + argType
        print " "*3, key, " ".join(args)

def handleArgs():
    if len(sys.argv) > 1 and commands.has_key(sys.argv[1]):
        cmd = commands[sys.argv[1]][0]
        long_opts = commands[sys.argv[1]][1:]
        (options, values) = getopt.gnu_getopt(sys.argv[2:], '', long_opts)
        #print options, values
        just_opts = map(lambda x: x[0], options)
        have_all = True
        for opt in long_opts:
            req = "--%s" % opt[:-1]
            #print req, opt, options, just_opts
            if not req in just_opts:
                have_all = False
                break
        if have_all:
            #print sys.argv[1], dict(options)
            return cmd(dict(options))
    usage()

if __name__ == '__main__':
    o = handleArgs()
